// Package network provides network optimization features for on-demand loading
package network

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/smart-snapshotter/internal/logging"
)

// ResumeManager manages download resume functionality
type ResumeManager struct {
	stateDir string
	mu       sync.RWMutex
	logger   logging.Logger
}

// DownloadState represents the state of a download for resume
type DownloadState struct {
	ID           string    `json:"id"`
	URL          string    `json:"url"`
	FilePath     string    `json:"file_path"`
	TempPath     string    `json:"temp_path"`
	TotalSize    int64     `json:"total_size"`
	Downloaded   int64     `json:"downloaded"`
	Checksum     string    `json:"checksum"`
	Chunks       []ChunkState `json:"chunks"`
	Status       string    `json:"status"`
	LastModified time.Time `json:"last_modified"`
	ETag         string    `json:"etag"`
	ResumeSupport bool   `json:"resume_support"`
}

// ChunkState represents the state of a chunk for resume
type ChunkState struct {
	ID         int   `json:"id"`
	Start      int64 `json:"start"`
	End        int64 `json:"end"`
	Downloaded int64 `json:"downloaded"`
	Status     string `json:"status"`
	TempFile   string `json:"temp_file"`
}

// NewResumeManager creates a new resume manager
func NewResumeManager(stateDir string, logger logging.Logger) (*ResumeManager, error) {
	if stateDir == "" {
		stateDir = "/tmp/smart-snapshotter/resume"
	}

	// Create state directory
	if err := os.MkdirAll(stateDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create resume state directory: %w", err)
	}

	return &ResumeManager{
		stateDir: stateDir,
		logger:   logger,
	}, nil
}

// SaveDownloadState saves the state of a download
func (rm *ResumeManager) SaveDownloadState(state *DownloadState) error {
	rm.mu.Lock()
	defer rm.mu.Unlock()

	state.LastModified = time.Now()

	stateFile := rm.getStateFilePath(state.ID)
	data, err := json.MarshalIndent(state, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal download state: %w", err)
	}

	if err := ioutil.WriteFile(stateFile, data, 0644); err != nil {
		return fmt.Errorf("failed to write download state: %w", err)
	}

	rm.logger.Debug("Saved download state", StringField("downloadID", state.ID), StringField("file", stateFile), StringField("size", fmt.Sprintf("%d", state.TotalSize)))
	return nil
}

// LoadDownloadState loads the state of a download
func (rm *ResumeManager) LoadDownloadState(downloadID string) (*DownloadState, error) {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	stateFile := rm.getStateFilePath(downloadID)
	data, err := ioutil.ReadFile(stateFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil // No state file exists
		}
		return nil, fmt.Errorf("failed to read download state: %w", err)
	}

	var state DownloadState
	if err := json.Unmarshal(data, &state); err != nil {
		return nil, fmt.Errorf("failed to unmarshal download state: %w", err)
	}

	rm.logger.Debug("Loaded download state", StringField("downloadID", downloadID), StringField("file", stateFile))
	return &state, nil
}

// DeleteDownloadState deletes the state of a download
func (rm *ResumeManager) DeleteDownloadState(downloadID string) error {
	rm.mu.Lock()
	defer rm.mu.Unlock()

	stateFile := rm.getStateFilePath(downloadID)
	if err := os.Remove(stateFile); err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("failed to delete download state: %w", err)
		}
	}

	rm.logger.Debug("Deleted download state", StringField("downloadID", downloadID), StringField("file", stateFile))
	return nil
}

// CanResume checks if a download can be resumed
func (rm *ResumeManager) CanResume(download *Download) (bool, *DownloadState) {
	state, err := rm.LoadDownloadState(download.ID)
	if err != nil || state == nil {
		return false, nil
	}

	// Check if download is still in progress
	if state.Status != "downloading" && state.Status != "paused" {
		return false, nil
	}

	// Check if temp file exists
	if _, err := os.Stat(state.TempPath); os.IsNotExist(err) {
		return false, nil
	}

	// Check if file size matches
	if state.TotalSize > 0 && download.Size > 0 && state.TotalSize != download.Size {
		return false, nil
	}

	// Check if URL matches
	if state.URL != download.URL {
		return false, nil
	}

	return true, state
}

// ResumeDownload resumes a download from saved state
func (rm *ResumeManager) ResumeDownload(download *Download) error {
	canResume, state := rm.CanResume(download)
	if !canResume || state == nil {
		return fmt.Errorf("download cannot be resumed")
	}

	rm.logger.Info("Resuming download", StringField("downloadID", download.ID),
		StringField("downloaded", fmt.Sprintf("%d", state.Downloaded)), StringField("total", fmt.Sprintf("%d", state.TotalSize)))

	// Restore download state
	download.Size = state.TotalSize
	download.Downloaded = state.Downloaded
	download.ResumeSupport = state.ResumeSupport
	download.TempPath = state.TempPath

	// Restore chunk states if available
	if len(state.Chunks) > 0 {
		download.Chunks = make([]*Chunk, len(state.Chunks))
		for i, chunkState := range state.Chunks {
			download.Chunks[i] = &Chunk{
				ID:         chunkState.ID,
				Start:      chunkState.Start,
				End:        chunkState.End,
				Downloaded: chunkState.Downloaded,
				Status:     chunkState.Status,
			}
		}
	}

	return nil
}

// CreateDownloadState creates download state from download
func (rm *ResumeManager) CreateDownloadState(download *Download) *DownloadState {
	state := &DownloadState{
		ID:            download.ID,
		URL:           download.URL,
		FilePath:      download.FilePath,
		TempPath:      download.TempPath,
		TotalSize:     download.Size,
		Downloaded:    download.Downloaded,
		Checksum:      download.Checksum,
		Status:        download.Status,
		ResumeSupport: download.ResumeSupport,
		LastModified:  time.Now(),
	}

	// Add chunk states if available
	if len(download.Chunks) > 0 {
		state.Chunks = make([]ChunkState, len(download.Chunks))
		for i, chunk := range download.Chunks {
			state.Chunks[i] = ChunkState{
				ID:         chunk.ID,
				Start:      chunk.Start,
				End:        chunk.End,
				Downloaded: chunk.Downloaded,
				Status:     chunk.Status,
			}
		}
	}

	return state
}

// UpdateDownloadState updates download state
func (rm *ResumeManager) UpdateDownloadState(download *Download) error {
	state := rm.CreateDownloadState(download)
	return rm.SaveDownloadState(state)
}

// CleanupOldStates removes old completed download states
func (rm *ResumeManager) CleanupOldStates(maxAge time.Duration) error {
	rm.mu.Lock()
	defer rm.mu.Unlock()

	files, err := ioutil.ReadDir(rm.stateDir)
	if err != nil {
		return fmt.Errorf("failed to read state directory: %w", err)
	}

	cutoffTime := time.Now().Add(-maxAge)
	removedCount := 0

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		// Check if file is old enough
		if file.ModTime().After(cutoffTime) {
			continue
		}

		// Load state to check if it's completed
		stateFile := filepath.Join(rm.stateDir, file.Name())
		data, err := ioutil.ReadFile(stateFile)
		if err != nil {
			continue
		}

		var state DownloadState
		if err := json.Unmarshal(data, &state); err != nil {
			continue
		}

		// Only remove completed downloads
		if state.Status == "completed" || state.Status == "failed" {
			if err := os.Remove(stateFile); err == nil {
				removedCount++
				rm.logger.Debug("Removed old download state", StringField("downloadID", state.ID), StringField("file", stateFile))
			}
		}
	}

	rm.logger.Info("Cleanup completed", StringField("removedCount", fmt.Sprintf("%d", removedCount)), StringField("maxAge", fmt.Sprintf("%v", maxAge)))
	return nil
}

// GetAllStates returns all download states
func (rm *ResumeManager) GetAllStates() ([]*DownloadState, error) {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	files, err := ioutil.ReadDir(rm.stateDir)
	if err != nil {
		return nil, fmt.Errorf("failed to read state directory: %w", err)
	}

	var states []*DownloadState
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		stateFile := filepath.Join(rm.stateDir, file.Name())
		data, err := ioutil.ReadFile(stateFile)
		if err != nil {
			continue
		}

		var state DownloadState
		if err := json.Unmarshal(data, &state); err != nil {
			continue
		}

		states = append(states, &state)
	}

	return states, nil
}

// getStateFilePath returns the path to the state file for a download
func (rm *ResumeManager) getStateFilePath(downloadID string) string {
	return filepath.Join(rm.stateDir, downloadID+".json")
}

// ResumeConfig contains resume configuration
type ResumeConfig struct {
	Enabled           bool          // Enable resume functionality
	StateDir          string        // Directory for resume state files
	MaxAge            time.Duration // Maximum age for resume states
	CleanupInterval   time.Duration // Interval for cleanup
	AutoResume        bool          // Automatically resume downloads on startup
}

// DefaultResumeConfig returns default resume configuration
func DefaultResumeConfig() *ResumeConfig {
	return &ResumeConfig{
		Enabled:         true,
		StateDir:        "/tmp/smart-snapshotter/resume",
		MaxAge:          24 * time.Hour,
		CleanupInterval: 1 * time.Hour,
		AutoResume:      true,
	}
}